
Consider innumerable clients connecting to a Java server over SSL sockets. 

Naturally, we generate certificates for each client, and perhaps import these self-signed certificates into our server truststore.

<!--break-->

<div style="border-bottom: solid 1px; background-color: #ccff00; font-size: 11pt; font-style: italic;"><img src="http://upload.wikimedia.org/wikipedia/commons/5/55/Gnome-security-medium.svg" border="0" align="left" hspace="0"/> Part of "The Enigma Posts"
</div>

<br>

In this article, we sign the client certificates using our server certificate, which is then our local CA root certificate. As such, our server must enable the revocation of certificates.

Ordinarily we create an SSLContext as follows.
<pre>
public class SSLContexts {
    ...
    public static SSLContext create(KeyStore keyStore, char[] keyPassword,
            KeyStore trustStore) throws Exception {
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keyStore, keyPassword);
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
        trustManagerFactory.init(trustStore);
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(keyManagerFactory.getKeyManagers(),
                trustManagerFactory.getTrustManagers(), new SecureRandom());
        return sslContext;
    }
}
</pre>Let's support local certificate revocation by means of a list of revoked CNs.
<pre>
public class RevocableSSLContexts {
    ...
    public static SSLContext create(KeyStore keyStore, char[] keyPass,
            KeyStore trustStore, List<String> revokedCNList) throws Exception {
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keyStore, keyPass);
        SSLContext sslContext = SSLContext.getInstance("TLS");
        TrustManager revocableTrustManager = new RevocableClientTrustManager(
                getPrivateKeyCertificate(keyStore),
                getX509TrustManager(trustStore),
                revokedCNList);
        sslContext.init(keyManagerFactory.getKeyManagers(),
                new TrustManager[]{revocableTrustManager},
                new SecureRandom());
        return sslContext;
    }
}
</pre>where we initialise a custom RevocableClientTrustManager with our server cert, and a collection of revoked certificates' CNs.
<pre>
public class RevocableClientTrustManager implements X509TrustManager {
    X509Certificate serverCertificate;
    X509TrustManager delegate;
    Collection<String> revokedCNList;
    ...
    @Override
    public void checkClientTrusted(X509Certificate[] certs, String authType) 
            throws CertificateException {
        if (!certs[0].getSubjectX500Principal().equals(
                serverCertificate.getSubjectX500Principal())) {
            throw new CertificateException("Server cert used as client cert");
        }
        if (!certs[0].getIssuerX500Principal().equals(
                serverCertificate.getSubjectX500Principal())) {
            throw new CertificateException("Untrusted issuer");
        }
        if (!Arrays.equals(certs[1].getPublicKey().getEncoded(),
                serverCertificate.getPublicKey().getEncoded())) {
            throw new CertificateException("Invalid server certificate");
        }
        if (revokedCNList.contains(getCN(certs[0].getSubjectDN()))) {
            throw new CertificateException("Certificate CN revoked");
        }
        delegate.checkClientTrusted(certs, authType);
    }
}
</pre>where we check that the client certificate is signed by our server certificate, and not revoked. Finally, we delegate to the standard X509TrustManager for good measure.

Notice that we do not accept the server cert as a client cert. This is a precaution in case our server keystore has been compromised, and a rogue is using it to connect to our server.

The collection of revoked certificates' identifiers might be read from a file or a database. This could be a synchronized list that can be updated on the fly, and so enables a dynamic truststore.
<pre>
        revokedCNList = Collections.synchronizedList(readRevocationList(crlFile));
        SSLContext sslContext = RevocableSSLContexts.create(keyStore, pass, trustStore, 
                revokedCNList);
        SSLServerSocket serverSocket = (SSLServerSocket) sslContext.
                getServerSocketFactory().createServerSocket(port);
</pre><h4>Certificate serial numbers</h4>

We see that <a href="http://www.docjar.org/html/api/sun/security/tools/KeyTool.java.html">KeyTool.java</a>, as used by the <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html">keytool</a> command-line utility, generates a random serial number as follows.
<pre>
        info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(
            new java.util.Random().nextInt() & 0x7fffffff));
</pre>In a later article, we might build a local CA management tool, which might use a sequence number for client certs which it generates programmatically and records them into an SQL database. In this case, we might revoke certificates by their serial numbers.
<pre>
public class RevocableClientTrustManager implements X509TrustManager {
    X509Certificate serverCertificate;
    X509TrustManager delegate;
    Collection<BigInteger> revokedSerialNumberList;
    ...
    @Override
    public void checkClientTrusted(X509Certificate[] certs, String authType) 
            throws CertificateException {
        ...
        if (revokedSerialNumberList.contains(certs[0].getSerialNumber())) {
            throw new CertificateException("Certificate serial number revoked");
        }
        delegate.checkClientTrusted(certs, authType);
    }
}
</pre>where <a href="http://www.docjar.org/html/api/java/security/cert/X509Certificate.java.html">X509Certificate</a>.getSerialNumber() returns a BigInteger.
<pre>
public abstract class X509Certificate extends Certificate {
   ...
   public abstract BigInteger getSerialNumber();
</pre>Incidently, this is implemented by <a href="http://www.docjar.org/html/api/sun/security/x509/X509CertImpl.java.html">X509CertImpl</a>.


<h4>keytool</h4>

Naturally we use <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html">keytool</a> to create our keystores and truststores. For example, we create a private SSL keystore for a client, say evanx, as follows.
<pre>
$ keytool -keystore evanx.jks -genkeypair -keyalg rsa -keysize 2048 -validity 365 -alias evanx \
    -dname "CN=evanx, OU=test"
</pre>where we specify a validity period of 365 days.
 
We export our certificate as follows.
<pre>
$ keytool -keystore evanx.jks -alias evanx -exportcert -rfc
</pre>We cut and paste the exported PEM text into a file, which we can inspect using openssl as follows.
<pre>
$ openssl x509 -text -in evanx.pem
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1380030508 (0x5241982c)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: CN=evanx, OU=test
        Validity
            Not Before: Sep 24 13:48:28 2013 GMT
            Not After : Sep 24 13:48:28 2014 GMT
        Subject: CN=evanx, OU=test
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
</pre>We import the client cert into the server SSL truststore.
<pre>
$ keytool -keystore server.trust.jks -alias evanx -importcert -file evanx.pem
</pre>Similarly, the server cert is imported into the clients' truststores.

Incidently, in a follow-up article we will create keystores programmatically for our unit tests, taking cues from <a href="http://www.docjar.org/html/api/sun/security/tools/KeyTool.java.html">KeyTool</a>, and emulating the manual procedure.

<h4>Client certificate signing</h4>

Alternatively, we might export a certificate signing request (CSR) as follows, and sign this with the server cert. In this case our server is a local CA where we will trust any of its issued certiticates. So we must support certificate revocation to control access.
<pre>
$ keytool -keystore evanx.jks -alias evanx -certreq
</pre>We cut and paste the output PEM text into a CSR file, and use Java7's keytool to sign this CSR, using its new -gencert option, which by the way is not available in earlier JRE's.
<pre>
$ keytool -keystore server.jks -gencert -validity 365 -rfc \
    -dname "CN=evanx, OU=test" -infile evanx.csr -outfile evanx.signed.pem
</pre>where we specify an unchanged DN and validity period for the newly signed certificate.

We can inspect the cert using openssl.
<pre>
$ openssl x509 -text -in evanx.signed.pem | grep CN
        Issuer: CN=server, OU=test
        Subject: CN=evanx, OU=test
</pre>Since the keystore requires its key certificate chain to be imported in the correct order starting with the root cert, we import the server cert first, and then our signed cert.
<pre>
$ keytool -keystore evanx.jks -importcert -noprompt \
    -file server.pem -alias server 
Enter keystore password: 
Certificate was added to keystore

$ keytool -keystore evanx.jks -importcert -noprompt \
    -file evanx.signed.pem -alias evanx
Enter keystore password: 
Certificate reply was installed in keystore
</pre>In this case, our client keystore can double up as our truststore since it happens to contain the server cert by virtue of its certificate chain, and naturally it must trust the server to which it wishes to connect. If the client cert is CA-signed, we must not use its keystore as its truststore, otherwise it would trust any server with a cert signed by that CA, since the CA certificate must be in the keystore as the root of the key certificate chain.

<img align="right" style="margin-left: 4px" src="http://jroller.com/evanx/resource/gnome-keys-250.png" />

Our server keystore can also double up as its truststore in this particular case, since naturally it contains the self-signed server cert (paired with its private key), and our client certs' CA cert is in fact this server cert.

If our server cert is CA-signed, then since the keystore must include its key certificate chain, it will include the CA root cert. Therefore if the keystore is misused as the trustore, then the server would trust <i>any</i> client cert signed by that CA.

<h4>Client certificate management</h4>

Clearly a certificate with a duplicate CN, impersonates the original certificate with that CN. So when issuing a client certificate, we must take care to ensure the uniqueness of the CN, and add the certificate or at least its unique identifier to a registry of some sort. 

If a certificate is signed but not recorded, or its record is deleted, our server is forever vulnerable to that rogue certificate. In order to review access, we clearly require a perfect record of all issued certificates. So we might record our signed certs into a keystore file as follows.
<pre>
$ keytool -keystore server.issued.jks -importcert -alias evanx -file evanx.pem 
Certificate was added to keystore
</pre>where this is not a truststore per se, but just a database of issued certificates. 

Interestingly, in trying to avoid a truststore containing all our client certificates, we have nevertheless ended up with one! 

<h4>Self-signed client certificates</h4>

All told, given the risk of a rogue certificate, we recommend using self-signed client certificates which are explicitly imported into our server truststore, where can be reviewed. Any issued certificate which goes unrecorded or is given a duplicate identifier by mistake or on purpose, is a vulnerability.

Note that when using self-signed client certs (or CA-signed server certs), the server keystore must not be used as its truststore, since the keystore naturally contains the server certificate, and so a rogue certificate can be created by signing it with the server key, e.g. using keytool -gencert, as illustrated above. 

Similarly, when using CA-signed server certs, the server keystore contains the CA root key, and so must not be used as the truststore. Otherwise any client cert signed by that CA would trusted, rather than just the client certs we have specifically added to the truststore.

<img align="right" src="http://jroller.com/evanx/resource/Gnome-application-certificate-250-crop.png">

<h4>Conclusion</h4>

We can sign client certificates using our server certificate, which is then our local CA root certificate. As such we should support certification revocation. So we introduce a custom SSL trust manager to support a local revocation list. Naturally this is only useful is if we can configure our server to use it!

In this special case, we can use our keystore as our truststore, which we argue is a practice that should be prohibited ordinarily.

We discuss the risk of rogue certificates, and recommend using self-signed client certificates whenever possible, rather than a local CA as presented here.

<h4>Furthermore</h4>

We'll perform unit testing of our custom trust manager in a follow-up article. Thereafter, we might implement a local CA management tool that records issued certificates, a local CA server supporting standard CRLs, and perhaps even a web-based admin tool for these goodies.

<h4>Other reading</h4>

Also relating to Java crypto in this blog: <a href="https://weblogs.java.net/blog/evanx/archive/2013/01/24/password-salt">Password Salt</a> for secure passwords; and leveraging the <a href="https://weblogs.java.net/blog/evanx/archive/2012/11/07/google-authenticator-thus-enabled">Google Authenticator</a> mobile app for multi-factor authentication for your own sites.
